package com.chukun.multi.guarded;


import com.chukun.multi.utils.Debug;

import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionVarBlocker implements Blocker {

    private final Lock lock;
    private final Condition condition;
    private final boolean allowAccess2lock;

    public ConditionVarBlocker(Lock lock){
        this(lock,true);
    }

    public  ConditionVarBlocker(Lock lock,boolean allowAccess2lock){
        this.lock = lock;
        this.allowAccess2lock = allowAccess2lock;
        condition = lock.newCondition();
    }

    public  ConditionVarBlocker(){
        this(false);
    }

    public Lock getLock(){
        if(allowAccess2lock){
            return this.lock;
        }
        throw new IllegalArgumentException("Access to the lock disallowed.");
    }
    public  ConditionVarBlocker(boolean allowAccess2lock){
        this(new ReentrantLock(),allowAccess2lock);
    }

    @Override
    public <V> V callWithGuard(GuardedAction<V> guardedAction) throws Exception {
        lock.lockInterruptibly();
        V result;
        try {
            final Predicate guard = guardedAction.guard;
            while (!guard.evaluate()){
                Debug.info("waiting.....");
                condition.await();
            }
            result = guardedAction.call();
            return result;
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void signalAfter(Callable<Boolean> stateOperation) throws Exception {
           lock.lockInterruptibly();
           try{
               if(stateOperation.call()){
                   condition.signal();
               }
           }finally {
               lock.unlock();
           }
    }

    @Override
    public void signal() throws InterruptedException {
        lock.lockInterruptibly();
        try{
            condition.signal();
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void broadcastAfter(Callable<Boolean> stateOperation) throws Exception {
         lock.lockInterruptibly();
         try{
             if(stateOperation.call()){
                 condition.signalAll();
             }
         }finally {
             lock.unlock();
         }
    }
}
